package com.yuuko.yuukoapi.framework.aspectj

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import org.tinylog.Logger
import java.util.*

@Aspect
@Component
class RequestLogAspect {
    private val objectMapper = ObjectMapper()

    // 定义切点：controller 包下的所有方法
    @Around("execution(* com.yuuko.yuukoapi.project..*.controller.*.*(..))")
    @Throws(Throwable::class)
    fun doAround(joinPoint: ProceedingJoinPoint): Any? {
        val attributes =
            RequestContextHolder.getRequestAttributes() as ServletRequestAttributes?
        if (attributes == null) return joinPoint.proceed()

        val request = attributes.request

        val startTime = System.currentTimeMillis()

        try {
            // 打印请求信息

            Logger.info("\n【请求开始】", "")
            Logger.info("IP         : {}", request.remoteAddr)
            Logger.info("URL        : {}", request.requestURL.toString())
            Logger.info("HTTP Method: {}", request.method)
            Logger.info(
                "Class Method: {}.{}",
                joinPoint.signature.declaringTypeName,
                joinPoint.signature.name
            )
            Logger.info("Args       : {}", getArgsAsString(joinPoint.getArgs()))

            // 执行原方法
            val result: Any? = joinPoint.proceed()

            // 打印返回结果
            Logger.info("Response   : {}", toJsonString(result))

            return result
        } finally {
            // 请求结束
            Logger.info("请求耗时     : {} ms", System.currentTimeMillis() - startTime)
            Logger.info("【请求结束】\n", "")
        }
    }

    @Throws(JsonProcessingException::class)
    private fun getArgsAsString(args: Array<Any?>?): String? {
        if (args == null || args.isEmpty()) return "[]"
        return Arrays.stream<Any?>(args)
            .map<String?> { obj: Any? -> this.toJsonString(obj) }
            .toList()
            .toString()
    }

    private fun toJsonString(obj: Any?): String? {
        return try {
            if (obj != null) objectMapper.writeValueAsString(obj) else null
        } catch (e: JsonProcessingException) {
            "[无法序列化]"
        }
    }

}